package struct;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PythonStruct {
    private static List<Object> unpackChar(byte[] bytes, ByteOrder bo, int start_index, int data_length, char data_type) {

        List<Object> results = new ArrayList<Object>();
        switch (data_type) {
            case '?':
                //布尔值比较特殊,因为字节不可分割,不满8位的布尔值也会占用一个字节,但可能只关心其中的几个字节,其它位舍弃
                while (data_length > 0) {
                    for (int bit = 0; bit < 8 && data_length > 0; bit++) {
                        byte bit_mask = (byte) (0x01 << bit);
                        Boolean value = (bit_mask == (bit_mask & bytes[start_index]));
                        results.add(value);
                        data_length--;
                    }
                    start_index += 1;
                }
                break;
            case 'x':
                //x 占位符.不解析
                break;
            case 'c':
                //ByteBuffer.wrap(bytes, start_index, data_length).getChar()
                while (data_length > 0) {
                    char value = (char) bytes[start_index];
                    results.add(value);
                    data_length -= 1;
                    start_index += 1;

                }
                break;
            case 'b':
                while (data_length > 0) {
                    int value = (int) bytes[start_index];
                    results.add(value);
                    data_length -= 1;
                    start_index += 1;
                }
                break;
            case 'B':
                while (data_length > 0) {
                    int value = (int) (bytes[start_index] & 0xff);
                    results.add(value);
                    data_length -= 1;
                    start_index += 1;
                }
                break;
            case 'h':
                while (data_length > 0) {
                    ByteBuffer buffer = ByteBuffer.wrap(bytes, start_index, 2).order(bo);
                    short value = buffer.getShort();
                    results.add(value);
                    data_length -= 1;
                    start_index += 2;
                }
                break;
            case 'H':
                while (data_length > 0) {
                    ByteBuffer buffer = ByteBuffer.wrap(bytes, start_index, 2).order(bo);
                    short value = buffer.getShort();
                    results.add(value & 0xFFFF);
                    data_length -= 1;
                    start_index += 2;
                }
                break;
            case 'i':
            case 'l':
                while (data_length > 0) {
                    ByteBuffer buffer = ByteBuffer.wrap(bytes, start_index, 4).order(bo);
                    int value = buffer.getInt();
                    results.add(value);
                    data_length -= 1;
                    start_index += 4;
                }
                break;
            case 'I':
            case 'L':
                while (data_length > 0) {
                    ByteBuffer buffer = ByteBuffer.wrap(bytes, start_index, 4).order(bo);
                    int value = buffer.getInt();
                    results.add(Integer.toUnsignedLong(value));
                    data_length -= 1;
                    start_index += 4;
                }
                break;
            case 'f':
                while (data_length > 0) {
                    ByteBuffer buffer = ByteBuffer.wrap(bytes, start_index, 4).order(bo);
                    float value = buffer.getFloat();
                    results.add(value);
                    data_length -= 1;
                    start_index += 4;
                }
                break;
            case 'q':
                while (data_length > 0) {
                    ByteBuffer buffer = ByteBuffer.wrap(bytes, start_index, 8).order(bo);
                    long value = buffer.getLong();
                    results.add(value);
                    data_length -= 1;
                    start_index += 8;
                }
                break;
            case 'Q':
                //Q占8位 long放不下 long.max=0x7fffffffffffffff
                while (data_length > 0) {
                    ByteBuffer buffer = ByteBuffer.wrap(bytes, start_index, 8).order(bo);
                    long value = buffer.getLong();
                    ByteBuffer byte_buffer = ByteBuffer.allocate(Long.BYTES);
                    byte_buffer.putLong(value);
                    BigInteger biv = new BigInteger(1, byte_buffer.array());
                    results.add(biv);

                    data_length -= 1;
                    start_index += 8;
                }
                break;
            case 'd':
                while (data_length > 0) {
                    ByteBuffer buffer = ByteBuffer.wrap(bytes, start_index, 8).order(bo);
                    double value = buffer.getDouble();
                    results.add(value);
                    data_length -= 1;
                    start_index += 8;
                }
                break;

            //default:
            //    throw new Exception("不支持的数据解析格式,仅xcbB?hHiIlLqQfd,见python struct.unpack");

        }
        return results;
    }

    /**
     * Format	c Type	Python	Note
     * x	pad byte	no value
     * c	char	string of length 1
     * b	signedchar	integer
     * B	unsignedchar	integer
     * ?	_Bool	bool	(1)
     * h	short	integer
     * H	unsignedshort	integer
     * i	int	integer
     * I	unsignedint	integer or long
     * l	long	integer
     * L	unsignedlong	long
     * q	longlong	long	(2)
     * Q	unsignedlonglong	long	(2)
     * f	float	float
     * d	double	float
     */
    public static StructValue unpack(String format, byte[] bytes) {

        List<Object> results = new ArrayList<Object>();

        //小端在前,除非特别声明<,默认大端在前
        Boolean le = true;

        String tmpFormat;
        int index = 0;

        if (format.startsWith(">")) {
            le = false;
            tmpFormat = format.substring(1);
        } else if (format.startsWith("<")) {
            tmpFormat = format.substring(1);
        } else {
            tmpFormat = format;
        }
        //正则检查
        //三部分 1前导大小端,可能有可能没有,2数字可能有可能多个,3字符指定元素之一
        Pattern p = Pattern.compile("^([\\d]*)([xcbB\\?hHiIlLqQfd])");

        while (!tmpFormat.isEmpty()) {
            Matcher m = p.matcher(tmpFormat);
            while (m.find()) {

                // 默认长度为1
                int dataLength = m.group(1).isEmpty() ? 1 : Integer.parseInt(m.group(1), 10);
                //类型
                char dataType = m.group(2).charAt(0);

                ByteOrder bo = le ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;

                results.addAll(PythonStruct.unpackChar(bytes, bo, index, dataLength, dataType));
                //java不支持传出参数?
                if (dataType == '?') {
                    index += dataLength / 8 + 1;
                } else if ("cxbB".indexOf(dataType) != -1) {
                    index += 1 * dataLength;
                } else if ("hH".indexOf(dataType) != -1) {
                    index += 2 * dataLength;
                } else if ("iIlLf".indexOf(dataType) != -1) {
                    index += 4 * dataLength;
                } else if ("qQd".indexOf(dataType) != -1) {
                    index += 8 * dataLength;
                }
                tmpFormat = tmpFormat.substring(m.group(0).length());
            }
        }

        return new StructValue(results);
    }

    public static void main(String[] args) {
        byte[] bytes = new byte[]{0x13, (byte) 0x80, 0x39, (byte) 0xff, (byte) 0x80, 0x00, 0x00, 0x01, 0x01};
        String[] formats = new String[]{
                "6?2?", //6个布尔,2个boolean,一共要两个字节
                "10?", //10boolean,一共要两个字节
                "x2c", //x 占位 2个字符
                "xcc", //x 占位 2个字符
                "xxx2b", //x 占位 2个单字节整型-128-127,有符号
                "3x2B", //x 占位 2个单字节整型0-255,无符号
                "4xh",  //大小端测试
                ">4xh", //大小端测试
                "<4xh",//大小端测试
                "4xH",//H
                "<4xH",//H
                ">4xH",//H
                "xIi",
                "<xIi",
                ">xIi",
                "xLl",
                "<xLl",
                ">xLl",
                "3?ff",
                "<3?ff",
                ">3?ff",
                "xq",
                "<xq",
                ">xq",
                "xQ",
                "<xQ",
                ">xQ",
                "xd",
                "<xd",
                ">xd"

        };

        for (String format : formats) {
            StructValue lists = PythonStruct.unpack(format, bytes);
            System.out.printf("%s=> %s=> %s\n", bytes.toString(), format, lists.values);
        }


    }
}


